﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows;

namespace Gmantis.Controls
{
    public delegate void MaskedTextChangedEventHandler(object sender, MaskedTextChangedEventArgs e);

    [EditorBrowsable(EditorBrowsableState.Always)]
    public class MaskedTextBox : TextBoxBase
    {
        // Fields
        private Flags _flags;
        private int _lastCorrectCursorPosition;
        private string _lastLoadedMask = "";
        private IList<string> _prevTextSymbols = new List<string>();
        private readonly TokenizedMask _tokenizedMask = new TokenizedMask();
        public static readonly DependencyProperty CultureProperty = DependencyProperty.Register("Culture", typeof(CultureInfo), typeof(MaskedTextBox), new PropertyMetadata(CultureInfo.CurrentCulture, new PropertyChangedCallback(MaskedTextBox.OnCulturePropertyChanged)));
        public static readonly DependencyProperty MaskProperty = DependencyProperty.Register("Mask", typeof(string), typeof(MaskedTextBox), new PropertyMetadata(new PropertyChangedCallback(MaskedTextBox.OnMaskPropertyChanged)));
        public static readonly DependencyProperty PromptCharProperty = DependencyProperty.Register("PromptChar", typeof(char), typeof(MaskedTextBox), new PropertyMetadata(new PropertyChangedCallback(MaskedTextBox.OnPromptCharPropertyChanged)));
        public static readonly DependencyProperty ReplaceModeProperty = DependencyProperty.Register("ReplaceMode", typeof(bool), typeof(MaskedTextBox), new PropertyMetadata(false));
        public static readonly DependencyProperty TextMaskFormatProperty = DependencyProperty.Register("TextMaskFormat", typeof(MaskFormat), typeof(MaskedTextBox), new PropertyMetadata(MaskFormat.IncludePrompt, new PropertyChangedCallback(MaskedTextBox.OnTextMaskFormatPropertyChanged)));
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(MaskedTextBox), new PropertyMetadata(new PropertyChangedCallback(MaskedTextBox.OnValuePropertyChanged)));

        // Events
        public event MaskedTextChangedEventHandler MaskedTextChanged;

        // Methods
        public MaskedTextBox()
        {
            base.DefaultStyleKey = typeof(TextBoxBase);
            this.SetCustomDefaultValues();
        }

        private void MaskedTextBox_Loaded(object sender, RoutedEventArgs e)
        {
            this.SetFlag(Flags.IsLoaded, true);
            this.UpdateAfterdDeferment();
        }

        private void MaskedTextBox_SelectionChanged(object sender, RoutedEventArgs e)
        {
            this._lastCorrectCursorPosition = base.SelectionStart;
        }

        private void MaskedTextBox_Unloaded(object sender, RoutedEventArgs e)
        {
            this.SetFlag(Flags.IsLoaded, false);
            this._lastLoadedMask = this.Mask;
        }

        private bool GetFlag(Flags flag)
        {
            return ((this._flags & flag) == flag);
        }

        private void OnCultureChanged(CultureInfo oldValue)
        {
            if (this.Culture == null)
            {
                this.Culture = oldValue;
            }
            else if (this._tokenizedMask.Culture != this.Culture)
            {
                this._tokenizedMask.Culture = this.Culture;
                this.UpdateText(0, true);
            }
        }

        private static void OnCulturePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MaskedTextBox box = d as MaskedTextBox;
            CultureInfo oldValue = (CultureInfo)e.OldValue;
            box.OnCultureChanged(oldValue);
        }

        private void OnMaskChanged(string old)
        {
            if (this.GetFlag(Flags.IsLoaded))
            {
                bool isEmpty = this._tokenizedMask.IsEmpty;
                string str = CompositeTextUtils.ConvertSymbolListToString(this._tokenizedMask.GetTextSymbols(MaskFormat.IncludePrompt, true)).TrimEnd(new char[] { ' ' });
                this._tokenizedMask.UpdateMask(CompositeTextUtils.ConvertToSymbolList(this.Mask), true);
                this.UpdatePromptFromMask();
                if (!isEmpty && this._tokenizedMask.IsEmpty)
                {
                    base.Text = str;
                }
                else
                {
                    if (isEmpty && !this._tokenizedMask.IsEmpty)
                    {
                        int num;
                        this._tokenizedMask.ProcessTextChange(0, null, CompositeTextUtils.ConvertToSymbolList(base.Text), 0, false, out num);
                    }
                    this.UpdateText(0, true);
                }
            }
        }

        protected virtual void OnMaskedTextChanged(MaskedTextChangedEventArgs e)
        {
            if (this.MaskedTextChanged != null)
            {
                this.MaskedTextChanged(this, e);
            }
        }

        private static void OnMaskPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MaskedTextBox box = d as MaskedTextBox;
            string oldValue = (string)e.OldValue;
            box.OnMaskChanged(oldValue);
        }

        private void OnPromptCharChanged(char old)
        {
            if (!this.GetFlag(Flags.IgnorePromptChange))
            {
                this._tokenizedMask.PromptPresenter = this.PromptChar.ToString();
                this.UpdateText(0, true);
            }
        }

        private static void OnPromptCharPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MaskedTextBox box = d as MaskedTextBox;
            char oldValue = (char)e.OldValue;
            box.OnPromptCharChanged(oldValue);
        }

        private void OnTextMaskFormatChanged(MaskFormat old)
        {
            this.UpdateValueProperty();
        }

        private static void OnTextMaskFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MaskedTextBox box = d as MaskedTextBox;
            MaskFormat oldValue = (MaskFormat)e.OldValue;
            box.OnTextMaskFormatChanged(oldValue);
        }

        internal override void OnTextTranslation()
        {
            base.OnTextTranslation();
            if (!this.GetFlag(Flags.IgnoreTextChange))
            {
                this.ProcessTextChangeAndUpdate();
            }
        }

        private void OnValueChanged(string old)
        {
            if (!this.GetFlag(Flags.IgnoreValueChange))
            {
                base.Text = this.Value ?? "";
            }
        }

        private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MaskedTextBox box = d as MaskedTextBox;
            string oldValue = (string)e.OldValue;
            box.OnValueChanged(oldValue);
        }

        internal override void OnWatermarkChanged(object oldValue)
        {
            base.OnWatermarkChanged(oldValue);
            base.UpdateIsWatermarked();
        }

        private TextProcessingInfo ProcessTextChange()
        {
            int num7 = 0;
            bool replaceMode = this.ReplaceMode;
            if (this._tokenizedMask.IsEmpty)
            {
                return new TextProcessingInfo(base.Text, base.SelectionStart, true);
            }
            IList<string> newTextArr = CompositeTextUtils.ConvertToSymbolList(base.Text);
            TextChangeInfo info = CompositeTextUtils.GetTextChange(this._prevTextSymbols, newTextArr, base.SelectionStart);
            List<string> replacedSymbols = new List<string>();
            List<string> addedSymbols = new List<string>();
            int deletedSymbolsCount = 0;
            int num2 = Math.Min(info.AddedLength, info.RemovedLength);
            for (int i = info.Offset; i < (info.Offset + num2); i++)
            {
                replacedSymbols.Add(newTextArr[i]);
            }
            if (info.AddedLength > info.RemovedLength)
            {
                int num4 = info.Offset + num2;
                int num5 = info.AddedLength - info.RemovedLength;
                for (int j = num4; j < (num4 + num5); j++)
                {
                    addedSymbols.Add(newTextArr[j]);
                }
            }
            else
            {
                deletedSymbolsCount = info.RemovedLength - info.AddedLength;
            }
            int selectionStart = base.SelectionStart;

            // num7 通过引用传递
            bool isSuccess = this._tokenizedMask.ProcessTextChange(info.Offset, replacedSymbols, addedSymbols, deletedSymbolsCount, replaceMode, out num7);
            return new TextProcessingInfo(CompositeTextUtils.ConvertSymbolListToString(this._tokenizedMask.GetTextSymbols()), num7, isSuccess);
        }

        private void ProcessTextChangeAndUpdate()
        {
            TextProcessingInfo info = this.ProcessTextChange();
            this.UpdateText(info.CursorPosition, info.IsSuccess);
        }

        private void SetCustomDefaultValues()
        {
            this.Culture = CultureInfo.CurrentCulture;
            this.PromptChar = '_';
            base.SelectionChanged += new RoutedEventHandler(this.MaskedTextBox_SelectionChanged);
            base.Loaded += new RoutedEventHandler(this.MaskedTextBox_Loaded);
            base.Unloaded += new RoutedEventHandler(this.MaskedTextBox_Unloaded);
            this.TextMaskFormat = MaskFormat.IncludePrompt;
        }

        private void SetFlag(Flags flag, bool on)
        {
            if (on)
            {
                this._flags |= flag;
            }
            else
            {
                this._flags &= ~flag;
            }
        }

        private void UpdateAfterdDeferment()
        {
            bool flag = CompositeTextUtils.ConvertSymbolListToString(this._prevTextSymbols) != base.Text;
            if ((this._lastLoadedMask != this.Mask) || flag)
            {
                this._tokenizedMask.UpdateMask(CompositeTextUtils.ConvertToSymbolList(this.Mask), false);
                this.UpdatePromptFromMask();
                flag = !this._tokenizedMask.IsEmpty;
            }
            if (flag)
            {
                int num;
                this._tokenizedMask.ProcessTextChange(0, null, CompositeTextUtils.ConvertToSymbolList(base.Text), 0, false, out num);
                this.UpdateText(0, true);
            }
            this._lastLoadedMask = this.Mask;
        }

        private void UpdatePromptFromMask()
        {
            if (this._tokenizedMask.PromptPresenter[0] != this.PromptChar)
            {
                this.SetFlag(Flags.IgnorePromptChange, true);
                try
                {
                    this.PromptChar = this._tokenizedMask.PromptPresenter[0];
                }
                finally
                {
                    this.SetFlag(Flags.IgnorePromptChange, false);
                }
            }
        }

        private void UpdateText(int cursorPosition, bool success)
        {
            MaskedTextChangedEventArgs e = null;
            string oldText = CompositeTextUtils.ConvertSymbolListToString(this._prevTextSymbols);
            if (this._tokenizedMask.IsEmpty)
            {
                this._prevTextSymbols = CompositeTextUtils.ConvertToSymbolList(base.Text);
                if (oldText != base.Text)
                {
                    e = new MaskedTextChangedEventArgs(base.Text, oldText);
                }
                this.UpdateValueProperty();
            }
            else
            {
                int selectionStart = base.SelectionStart;
                this.SetFlag(Flags.IgnoreTextChange, true);
                try
                {
                    IList<string> textSymbols = this._tokenizedMask.GetTextSymbols();
                    string newText = CompositeTextUtils.ConvertSymbolListToString(textSymbols);
                    if (base.Text != newText)
                    {
                        base.Text = newText;
                        if (!success)
                        {
                            base.SelectionStart = this._lastCorrectCursorPosition;
                        }
                        else
                        {
                            if ((selectionStart != 0) && (cursorPosition >= 0))
                            {
                                base.SelectionStart = cursorPosition;
                            }
                            this._lastCorrectCursorPosition = base.SelectionStart;
                        }
                    }
                    this._prevTextSymbols = textSymbols;
                    if (oldText != newText)
                    {
                        e = new MaskedTextChangedEventArgs(newText, oldText);
                    }
                    this.UpdateValueProperty();
                }
                finally
                {
                    this.SetFlag(Flags.IgnoreTextChange, false);
                }
            }
            if (e != null)
            {
                this.OnMaskedTextChanged(e);
            }
        }

        public void UpdateValue()
        {
            if (!this.GetFlag(Flags.IsLoaded))
            {
                this.SetFlag(Flags.IsLoaded, true);
                try
                {
                    this.UpdateAfterdDeferment();
                }
                finally
                {
                    this.SetFlag(Flags.IsLoaded, false);
                }
            }
        }

        private void UpdateValueProperty()
        {
            this.SetFlag(Flags.IgnoreValueChange, true);
            try
            {
                string str = this._tokenizedMask.IsEmpty ? base.Text : CompositeTextUtils.ConvertSymbolListToString(this._tokenizedMask.GetTextSymbols(this.TextMaskFormat));
                if (this.Value != str)
                {
                    this.Value = str;
                }
            }
            finally
            {
                this.SetFlag(Flags.IgnoreValueChange, false);
            }
        }

        // Properties
        public CultureInfo Culture
        {
            get { return (CultureInfo)GetValue(CultureProperty); }
            set { SetValue(CultureProperty, value); }
        }

        public bool IsValid
        {
            get
            {
                return this._tokenizedMask.GetIsValid();
            }
        }

        public override bool IsWatermarked
        {
            get
            {
                bool flag = ((this._tokenizedMask.IsEmpty ? string.IsNullOrEmpty(base.Text) : (this._tokenizedMask.GetTextSymbols(MaskFormat.ExcludePromptAndLiterals).Count == 0))
                    && (base.Watermark != null)) && (base.Watermark.ToString().Length > 0);
                if (base.IsReadOnly)
                {
                    return flag;
                }
                return (!base.IsFocused && flag);
            }
        }

        public string Mask
        {
            get { return (string)GetValue(MaskProperty); }
            set { SetValue(MaskProperty, value); }
        }

        [TypeConverter(typeof(CharConverter))]
        public char PromptChar
        {
            get { return (char)GetValue(PromptCharProperty); }
            set { SetValue(PromptCharProperty, value); }
        }

        public bool ReplaceMode
        {
            get { return (bool)GetValue(ReplaceModeProperty); }
            set { SetValue(ReplaceModeProperty, value); }
        }

        public MaskFormat TextMaskFormat
        {
            get { return (MaskFormat)GetValue(TextMaskFormatProperty); }
            set { SetValue(TextMaskFormatProperty, value); }
        }

        public string Value
        {
            get { return (string)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        // Nested Types
        private enum Flags
        {
            IgnorePromptChange = 0x20,
            IgnoreTextChange = 1,
            IgnoreValueChange = 2,
            IsLoaded = 4,
            LASTCOMMON = 2
        }

        private class TextProcessingInfo
        {
            // Methods
            public TextProcessingInfo(string text, int cursorPosition, bool isSuccess)
            {
                this.Text = text;
                this.CursorPosition = cursorPosition;
                this.IsSuccess = isSuccess;
            }

            // Properties
            public int CursorPosition { get; private set; }

            public bool IsSuccess { get; private set; }

            public string Text { get; private set; }
        }
    }
}
